using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace Dida
{
	public class State<GlueTrait>
		where GlueTrait : StateGlueTrait
	{
		TraitInstance traitInstance;
		GlueTrait backend;

		IStateMachine<GlueTrait> stateMachine;

		public State(TraitInstance traitInstance,IStateMachine<GlueTrait> stateMachine)
		{
			this.traitInstance = traitInstance;
			this.backend = traitInstance.GetTrait<GlueTrait>();

			this.stateMachine = stateMachine;
		}

		public virtual void OnActivated(StateActivatedReason reason)
		{
			backend.OnActivated(reason);
		}

		public virtual void OnDeactivated(StateDeactivatedReason reason)
		{
			backend.OnDeactivated(reason);
		}

		public GlueTrait Glue
		{
			get { return backend; }
		}
	}

	public class StateMachine<GlueTrait> : IStateMachine<GlueTrait>
		where GlueTrait : StateGlueTrait
	{
		StateMachineTemplate<GlueTrait> template;

		List<State<GlueTrait>> states = new List<State<GlueTrait>>();

		internal StateMachine(StateMachineTemplate<GlueTrait> template)
		{
			this.template = template;
		}

		public void GoToState(State<GlueTrait> state)
		{
			if (states.Count != 0)
			{
				states[states.Count - 1].OnDeactivated(StateDeactivatedReason.GoToOtherState);

				states[states.Count - 1] = state;
				state.OnActivated(StateActivatedReason.GoToState);
			}
			else
			{
				state.OnActivated(StateActivatedReason.GoToState);
				states.Add(state);
			}
		}

		public void PushState(State<GlueTrait> state)
		{
			// deactivate the state that's currently on top of the stack
			states[states.Count - 1].OnDeactivated(StateDeactivatedReason.OtherStatePushed);

			states.Add(state);

			// activate the state we just pushed
			state.OnActivated(StateActivatedReason.Pushed);
		}

		public void PopState()
		{
			Debug.Assert(states.Count > 1, "No state to pop");

			// deactivate the state we are going to pop
			states[states.Count - 1].OnDeactivated(StateDeactivatedReason.Popped);

			states.RemoveAt(states.Count - 1);

			// activate the new top
			states[states.Count - 1].OnActivated(StateActivatedReason.OtherStatePopped);
		}

		public State<GlueTrait> CurState
		{
			get
			{
				Debug.Assert(states.Count != 0, "There's no current state");
				return states[states.Count - 1];
			}
		}

		public State<GlueTrait> CreateStateInstance(string stateName)
		{
			return template.CreateStateInstance(stateName, this);
		}

		public GlueTrait GetCurStateGlue()
		{
			return CurState.Glue;
		}

		public void Start(IStateMachineUser<GlueTrait> user)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public void Stop()
		{
			throw new Exception("The method or operation is not implemented.");
		}
	}

	public abstract class StateGlueTrait : Trait
	{
		private IStateMachine stateMachine;

		internal void SetStateMachine(IStateMachine stateMachine)
		{
			this.stateMachine = stateMachine;
		}
		
		[TraitDeclareMethod(TraitMethodAcceptance.MultipleImplementations)]
		public virtual void OnActivated(StateActivatedReason reason) { }

		[TraitDeclareMethod(TraitMethodAcceptance.MultipleImplementations)]
		public virtual void OnDeactivated(StateDeactivatedReason reason) { }

		[TraitImplementMethod(NeedsDeclaration = false)]
		public IStateMachine GetStateMachineImpl()
		{
			return stateMachine;
		}
	}
}